package com.yc.commons;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DBHelper {

    static{
        try {
            //Class.forName("oracle.jdbc.OracleDriver");
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    //获取一个Connection
    public Connection getConnection(   ) throws SQLException {

        DbProperties p=DbProperties.getInstance();
//        Connection con= DriverManager.getConnection(  p.getProperty("oracleurl"),
//                                                      p.getProperty("oracleuname"),
//                                                       p.getProperty("oraclepwd")
//                                                 );
        Connection con= DriverManager.getConnection(  p.getProperty("mysqlurl"),
                p.getProperty("mysqlname"),
                p.getProperty("mysqlpwd")
        );
        return con;
    }

    /**
     * 封装更新( 增加insert, update更新,  delete删除 )
     * sql:是要执行的 更新语句, 这语句有 n个 ?占位符，及对应的n个参数
     *    Object...动态数组， 这个数组的长度不确定，这种参数只能加在一个方法参数列表的最后
     *    sql:  update emp set ename=? , mgr=? where empno=?
     *    params:     '张三','李四', 1101
     */
    public int doUpdate( String sql,  Object... params    ){
        int result=-1;
        try(
            Connection con=getConnection();//获取联接
            PreparedStatement pstmt=con.prepareStatement(   sql );  //预编译语句对象
        ) {
            setParams(pstmt,params );
            //执行语句
            result = pstmt.executeUpdate();
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return result;
    }
    public int doUpdateKey(String sql, Object... params) {
        int generatedKey = -1;
        try (
                Connection con = getConnection();
                PreparedStatement pstmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        ) {
            setParams(pstmt, params);
            int affectedRows = pstmt.executeUpdate();

            if (affectedRows > 0) {
                // 获取生成的主键
                try (ResultSet rs = pstmt.getGeneratedKeys()) {
                    if (rs.next()) {
                        generatedKey = rs.getInt(1); // 假设主键是整数类型
                    }
                }
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        return generatedKey;
    }

    private  void setParams(PreparedStatement pstmt,Object... params ) throws SQLException {
        //问题一:  ?对应的参数类型是什么，这个类型是什么，则  setXxx()????
        // =>   将所有的参数类型指定为  Object，  =》  setObject();
        //问题二:  总共有几个参数???params到底有几个.
        //   params是动态数组，  length
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                pstmt.setObject(i + 1, params[i]);
            }
        }
    }

    /**
     * 基于模板设计模式的查询方法.
     * @param rowMapper: 对一行结果集的处理，返回一个对应的对象
     * @param sql
     * @param params
     * @return
     * @param <T>
     */
    public <T> List<T>  select(  RowMapper<T> rowMapper,   String sql, Object...params  ) throws SQLException {
        List<T> list=new ArrayList<>();
        //查询步骤的模板
        try(
                Connection con=getConnection();
                PreparedStatement pstmt=con.prepareStatement(sql);) {
            this.setParams(pstmt, params);
            ResultSet rs = pstmt.executeQuery();
            int num = 0;
            while (rs.next()) {
                //结果集每一行的处理，由RowMapper接口的实现决定   <=程序员
                T t = rowMapper.mapRow(rs, num);
                num++;
                list.add(t);
            }
        }catch(Exception ex){
            ex.printStackTrace();
            throw ex;
        }
        return list;
    }




    /**  方法名相同，参数不同=>重载方法
     * 查询返回值是一个  List<T>  T代表任意的类的对象.
     *     T类标准javaBean: 属性封装(private), 对外提供 get/set    setXxxx(参数)
     * @param c: 代表  T 类的反射类的对象 (T基因 )
     * @param sql
     * @param params
     * @return
     * @param <T>
     */
    //  List<Dept> list=db.select(   Dept.class, sql    );
    public <T> List<T> select(Class<T> c, String sql, Object... params) throws InstantiationException, IllegalAccessException, InvocationTargetException {
        List<T> result = new ArrayList<>();
        List<Map<String, Object>> list = this.select(sql, params);

        for (Map<String, Object> map : list) {
            T t = null; // 使用更通用的方式创建实例
            try {
                t = c.getDeclaredConstructor().newInstance();
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }

            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                // 处理属性名
                String methodName = "set" + Character.toUpperCase(key.charAt(0)) + key.substring(1);

                try {
                    Method setMethod = c.getMethod(methodName, value.getClass());
                    setMethod.invoke(t, value); // 使用反射动态调用 set 方法
                } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
                    // 处理异常情况
                    e.printStackTrace(); // 可以根据具体情况选择如何处理异常
                }
            }

            result.add(t);
        }
        return result;
    }


    /**
     * 从c中找出  名字为  methodName的方法
     * @param methodName
     * @param c
     * @return
     * @param <T>
     */
    private <T> Method findSetMethod(String methodName, Class<T> c) {
    	
        Method[] ms=c.getDeclaredMethods();
        for(   Method m:ms){
            if(  methodName.equals( m.getName() )){
                return m;
            }
        }
        return null;
    }
    
    

    /**
     * 查询
     * @param sql
     * @param params
     */
    public List<Map<String,Object>> select(String sql, Object...params){
        List<Map<String,Object>> list=new ArrayList<>();
        try(
                Connection con=getConnection();//获取联接
                PreparedStatement pstmt=con.prepareStatement(   sql );  //预编译语句对象
        ) {
            setParams(pstmt,params );
            ResultSet rs=pstmt.executeQuery();
            // jdbc中规范  ResultSet中有关于结果集的一切信息
            ResultSetMetaData rsmd=rs.getMetaData();   //结果集元数据   => 有多少个列  ,每个列叫什么名字
             int columnCount= rsmd.getColumnCount();  //列的数量
            //循环结果集的行.
            while(  rs.next() ){
                Map<String,Object> map=new HashMap<String,Object>();  //一行就是一个map
                 //那么到底有几个列啊????
                for( int i=0;i<columnCount;i++){
                    //数据类型
                    //System.out.print( rs.getObject(   i+1 )+"\t");   // TODO:  这个数据不能这样处理...
                    map.put(   rsmd.getColumnName(i+1),   rs.getObject(   i+1)   );   //存每一列...
                }
                list.add( map );  //将这个map存到 list
               // System.out.println();
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return list;
    }

}
